Day 18 最後留下一段很常見的 React 寫法:
{user && <Member />}
&& 是「AND」。
以前看到它,我腦中大概只會出現這種題目:
true && true
結果當然是:
true
但 React 裡這段就很怪:
user && <Member />
左邊是會員資料,右邊甚至是一個 React Component。
它們都不是單純的 true 或 false。
為什麼一個邏輯運算子,最後可以變成畫面?
&& 不一定回傳 boolean我原本把 && 想成:
檢查左右兩邊
↓
回傳 true 或 false
這個理解不算完全錯,但少了一層。
JavaScript 的 && 會這樣做:
左邊是 falsy
→ 停下來,回傳左邊原本的值
左邊是 truthy
→ 繼續看右邊,回傳右邊原本的值
所以 && 會利用 truthy、falsy 做判斷,但最後回傳的不一定是 boolean。
它可能回傳字串、數字、Object,也可能是 JSX。
這就是 React 能寫:
{user && <Member />}
的關鍵。
先在瀏覽器 Console 試這幾行:
console.log(true && '會員中心')
console.log(false && '會員中心')
console.log({ name: 'Anthony' } && '會員中心')
console.log(null && '會員中心')
會得到:
會員中心
false
會員中心
null
如果 && 一定只會回傳 boolean,第一行和第三行應該都要得到 true。
但實際結果不是。
第一行:
true && '會員中心'
左邊是 truthy,所以 JavaScript 繼續看右邊,最後回傳:
會員中心
第二行:
false && '會員中心'
左邊已經是 falsy,不管右邊是什麼,整個條件都不可能成立。
所以 JavaScript 直接停下來,回傳:
false
第三行的 Object 是 truthy,因此繼續往右走,最後一樣回傳字串。
第四行的 null 是 falsy,所以直接回傳 null。
原來 && 判斷的時候會看真假,但它留下來的是原本的值。
這叫做 short-circuit evaluation,中文常翻成「短路求值」。
名字聽起來有點像電路燒掉,但它其實只是在說:
前面的結果已經足以決定要不要繼續,JavaScript 就不再計算後面。
可以做一個更明顯的實驗:
function openMemberPage() {
console.log('右邊真的執行了')
return '會員中心'
}
const guest = null
const member = { name: 'Anthony' }
console.log(guest && openMemberPage())
console.log(member && openMemberPage())
第一行:
guest && openMemberPage()
guest 是 null,左邊已經是 falsy,所以 openMemberPage() 根本不會執行。
第二行的 member 是 Object,屬於 truthy,JavaScript 才會繼續執行右邊的 function。
流程可以畫成:
先計算左邊
↓
左邊是 falsy?
├─ 是 → 回傳左邊,右邊不執行
└─ 否 → 執行右邊,回傳右邊的值
現在回來看:
{user && <Member />}
假設還沒登入:
const user = null
這段 expression 會變成:
null && <Member />
左邊是 falsy,所以 JavaScript 直接回傳 null。
在這個位置,React 不會把 null 顯示成畫面。
因此 <Member /> 不會出現。
登入後:
const user = {
name: 'Anthony'
}
這時候 user 的值是 Object。概念上,expression 會像這樣:
({ name: 'Anthony' }) && <Member />
Object 是 truthy,所以 JavaScript 繼續計算右邊,整段 expression 最後得到:
<Member />
React 收到這個結果後,才 render Member Component。
所以不是 && 自己會畫 React 畫面。
真正發生的是:
JavaScript 計算 user && <Member />
↓
沒有 user → 結果是 null
有 user → 結果是 <Member />
↓
React render JavaScript 算出的結果
先做一個會員資料 Component:
function MemberDetails() {
return (
<section>
<h2>會員資料</h2>
<p>姓名:Anthony</p>
</section>
)
}
再用 state 決定要不要顯示它:
import { useState } from 'react'
function MemberDetails() {
return (
<section>
<h2>會員資料</h2>
<p>姓名:Anthony</p>
</section>
)
}
function App() {
const [showDetails, setShowDetails] = useState(false)
function toggleDetails() {
setShowDetails(!showDetails)
}
return (
<main>
<button onClick={toggleDetails}>
顯示/隱藏會員資料
</button>
{showDetails && <MemberDetails />}
</main>
)
}
export default App
第一次 render:
showDetails = false
↓
false && <MemberDetails />
↓
結果是 false
↓
會員資料不顯示
按下按鈕後:
setShowDetails(!showDetails)
↓
showDetails 變成 true
↓
React 再 render
↓
true && <MemberDetails />
↓
結果是 <MemberDetails />
↓
會員資料出現在畫面上
這裡又把前幾天的東西串起來了:
State
→ 記住現在要不要顯示
Event
→ 按下按鈕時更新 state
&&
→ 根據目前的 state 決定要不要交出 JSX
? : 和 && 到底差在哪裡?Day 18 寫的是:
{user ? <Member /> : <Login />}
它的情境是二選一:
有 user
→ 顯示 Member
沒有 user
→ 顯示 Login
今天的:
{user && <MemberMenu />}
比較像只有一個選項:
有 user
→ 顯示 MemberMenu
沒有 user
→ 什麼都不顯示
所以不用把兩種寫法背成誰比較好。
先問畫面需求:
A、B 二選一
→ 用 ternary 比較直接
條件成立才多顯示一塊
→ && 通常比較簡潔
0 可能真的會顯示出來假設要顯示未讀訊息:
const unreadCount = 0
return (
<div>
{unreadCount && (
<p>你有 {unreadCount} 則未讀訊息</p>
)}
</div>
)
我可能會直覺以為:
0 是 falsy
→ 所以什麼都不顯示
但 JavaScript 算出的結果其實是:
0 && <p>...</p>
// 0
&& 回傳的是左邊原本的 0,而 React 會把數字 0 顯示出來。
於是畫面可能真的多出一個:
0
可以把條件寫得更明確:
{unreadCount > 0 && (
<p>你有 {unreadCount} 則未讀訊息</p>
)}
這次左邊一定會得到真正的 boolean:
unreadCount > 0
當未讀數量是 0,結果是 false,React 就不會顯示那段訊息。
所以不能只背:
falsy 就不會出現在 React 畫面上。
更準確的說法是:
&&會回傳原本的值;那個值最後會不會顯示,還要看 React 怎麼處理它。
&& 的結果一定是 true 或 false?不一定。
它用 truthy、falsy 決定是否繼續,但回傳的是其中一邊原本的值。
&& 是 React 的條件渲染語法?不是。
&& 本來就是 JavaScript 的 logical AND。React 只是把 JavaScript 算出的結果拿去 render。
不能這樣一概而論。
例如 null 和 false 在這裡不會形成可見內容,但數字 0 會顯示出來。
&& 可以完全取代 ternary?不行。
&& 適合「成立才顯示」;ternary 適合「成立顯示 A,否則顯示 B」。
我會記這個流程:
左邊是 falsy
→ && 回傳左邊
左邊是 truthy
→ && 繼續計算並回傳右邊
所以:
{user && <Member />}
不是一條 React 魔法咒語。
它只是 JavaScript 先決定最後要交出:
null / false 等結果
或是:
<Member />
React 再把收到的結果 render 出來。